﻿using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using StardewValley.Quests;

namespace WorseGame.Content
{
    public class 任务过期(IMonitor 监视器, IModHelper 助手)
    {
        private readonly IMonitor 监视器 = 监视器;
        private readonly IModHelper 助手 = 助手;
        private const int 基础过期天数 = 5;
        private const int 最大过期天数 = 10;
        private Dictionary<string, int> 任务发布时间 = [];

        public void Initialize()
        {
            助手.Events.GameLoop.DayStarted += 每日开始时;
            助手.Events.GameLoop.SaveLoaded += 存档加载时;
            助手.Events.GameLoop.UpdateTicked += 更新时触发;
        }

        private void 存档加载时(object? sender, SaveLoadedEventArgs e)
        {
            加载任务数据();
        }

        private void 每日开始时(object? sender, DayStartedEventArgs e)
        {
            检查任务过期();
        }

        private void 更新时触发(object? sender, UpdateTickedEventArgs e)
        {
            if (Context.IsWorldReady)
            {
                检测新任务();
            }
        }

        private void 检测新任务()
        {
            foreach (var 任务 in Game1.player.questLog)
            {
                if (任务 != null && !string.IsNullOrEmpty(任务.questTitle))
                {
                    string 任务标识 = $"{任务.questTitle}_{任务.currentObjective}";

                    if (!任务发布时间.ContainsKey(任务标识))
                    {
                        任务发布时间[任务标识] = (int)Game1.stats.DaysPlayed;
                    }
                }
            }

            保存任务数据();
        }

        private void 检查任务过期()
        {
            int 当前天数 = (int)Game1.stats.DaysPlayed;
            var 要移除的任务 = new List<Quest>();

            foreach (var 任务 in Game1.player.questLog.ToList())
            {
                if (任务 != null)
                {
                    string 任务标识 = $"{任务.questTitle}_{任务.currentObjective}";

                    if (任务发布时间.TryGetValue(任务标识, out int 发布时间))
                    {
                        int 已过去天数 = 当前天数 - 发布时间;

                        if (已过去天数 >= 基础过期天数)
                        {
                            float 过期概率 = 计算过期概率(已过去天数);
                            var 随机数 = new Random((int)Game1.uniqueIDForThisGame + 当前天数 + 任务标识.GetHashCode());

                            if (随机数.Next(100) < 过期概率)
                            {
                                任务过期了(任务, 已过去天数);
                                要移除的任务.Add(任务);
                            }
                            else if (已过去天数 >= 最大过期天数)
                            {
                                任务过期了(任务, 已过去天数);
                                要移除的任务.Add(任务);
                            }
                            else if (已过去天数 >= 基础过期天数 + 3)
                            {
                                显示任务警告(任务, 已过去天数);
                            }
                        }
                    }
                }
            }
            foreach (var 任务 in 要移除的任务)
            {
                Game1.player.questLog.Remove(任务);
                移除任务记录(任务);
            }
        }

        private static float 计算过期概率(int 已过去天数)
        {
            int 超出天数 = 已过去天数 - 基础过期天数;
            float 基础概率 = 20f;
            float 额外概率 = 超出天数 * 15f;

            return Math.Min(基础概率 + 额外概率, 100f);
        }

        private static void 任务过期了(Quest 任务, int 已过去天数)
        {
            string 任务名称 = 任务.questTitle;
            string npc名称 = 提取NPC名称(任务名称) ?? "委托人";
            string 过期消息 = $"{npc名称} 的任务「{任务名称}」因为太久没有完成而失效了";
            Game1.addHUDMessage(new HUDMessage(过期消息, 3));
            减少好感度(npc名称);
        }

        private static void 显示任务警告(Quest 任务, int 已过去天数)
        {
            string 任务名称 = 任务.questTitle;
            string npc名称 = 提取NPC名称(任务名称) ?? "委托人";
            if (已过去天数 % 3 == 0)
            {
                string 警告消息 = $"{npc名称} 的任务「{任务名称}」已经拖延了 {已过去天数} 天！";
                Game1.addHUDMessage(new HUDMessage(警告消息, 2));
            }
        }

        private static string? 提取NPC名称(string 任务名称)
        {
            var npc列表 = new List<string> { "亚历克斯", "艾利欧特", "哈维", "山姆", "塞巴斯蒂安", "谢恩", "阿比盖尔", "艾米丽", "海莉", "莉亚", "玛鲁", "潘妮", "卡洛琳", "克林特", "德米特里厄斯", "矮人", "艾芙琳", "乔治", "格斯", "贾斯", "乔迪", "肯特", "科罗布斯", "刘易斯", "莱纳斯", "玛妮", "潘姆", "皮埃尔", "罗宾", "桑迪", "文森特", "威利", "法师", "雷欧", "门卫", "吉尔", "州长", "爷爷", "冈瑟", "仆从", "马龙", "莫里斯", "齐先生", "贝啼", "蜗牛教授", "菲兹" };

            foreach (var npc in npc列表)
            {
                if (任务名称.Contains(npc))
                {
                    return npc;
                }
            }

            return null;
        }

        private static void 减少好感度(string? npc名称)
        {
            if (npc名称 != null)
            {
                foreach (var npc in Utility.getAllCharacters())
                {
                    if (npc.Name == npc名称 && npc.IsVillager)
                    {
                        var 随机数 = new Random((int)Game1.uniqueIDForThisGame + (int)Game1.stats.DaysPlayed);
                        int 减少量 = 50 + 随机数.Next(51);

                        Game1.player.changeFriendship(-减少量, npc);
                        break;
                    }
                }
            }
        }

        private void 移除任务记录(Quest 任务)
        {
            string 任务标识 = $"{任务.questTitle}_{任务.currentObjective}";
            任务发布时间.Remove(任务标识);
            保存任务数据();
        }

        private void 保存任务数据()
        {
            var 数据列表 = new List<string>();
            foreach (var kvp in 任务发布时间)
            {
                数据列表.Add($"{kvp.Key}:{kvp.Value}");
            }

            string 保存数据 = string.Join(";", 数据列表);
            助手.Data.WriteSaveData("TaskExpirationData", 保存数据);
        }

        private void 加载任务数据()
        {
            string? 保存数据 = 助手.Data.ReadSaveData<string>("TaskExpirationData");
            if (!string.IsNullOrEmpty(保存数据))
            {
                任务发布时间.Clear();
                var 数据数组 = 保存数据.Split(';');
                foreach (var 数据项 in 数据数组)
                {
                    if (!string.IsNullOrEmpty(数据项))
                    {
                        var 键值对 = 数据项.Split(':');
                        if (键值对.Length == 2)
                        {
                            任务发布时间[键值对[0]] = int.Parse(键值对[1]);
                        }
                    }
                }
            }
        }
    }
}